Skip to content

Remove legacy interaction formats and state identity - #36

Merged
Chinmay-KB merged 11 commits into
mainfrom
codex/slim-session-lifecycle-payloads
Aug 11, 2026
Merged

Remove legacy interaction formats and state identity#36
Chinmay-KB merged 11 commits into
mainfrom
codex/slim-session-lifecycle-payloads

Conversation

@Chinmay-KB

@Chinmay-KB Chinmay-KB commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Send static app, device, IP, and locale context only with session_start.
  • Send only changed identity and trait fields on later lifecycle events.
  • Remove SDK-written stateAnchor, stateSignature, and state_change data.
  • Remove legacy gesture formats, publication modes, aliases, and compatibility constructors.
  • Keep encoded screenshot capture for every interaction.
  • Publish canonical tap, swipe, and scroll interactions with capture provenance.
  • Preserve completed gestures across session and lifecycle boundaries.
  • Rebuild tap semantics from the current inventory and reset scroll semantics per gesture.
  • Increase the session wire schema version to 10.
  • Release tugboat and tugboat_dio as version 0.8.0.

Compatibility

  • Session writers and readers support schema version 10 only.
  • The old public state model types and legacy gesture formats are removed.
  • This breaking change is allowed because the SDK has no external production consumers.
  • The collector change must deploy before SDK 0.8.0 is released.
  • The collector accepts events that omit the removed stateAnchor field.

Review fixes

  • Preserve explicit user_changed identity clears.
  • Finalize completed gestures when a session ends, a session is replaced, or the app leaves the active lifecycle.
  • Give one scroll gesture one stable pointer owner during multi-pointer input.
  • Reset semantic scroll accumulation before unavailable emission paths.
  • Preserve repeated semantic slices in each new scroll gesture.
  • Preserve route causeEventId in the flat collector envelope.
  • Restrict overlay navigation assertions to events created after each action.
  • Increase the wire schema version after the event contract change.

Validation

  • flutter analyze
  • flutter test --concurrency=1 test/collector_mapper_test.dart test/replay/replay_overlay_nested_navigation_matrix_test.dart test/viewport_semantic_map_test.dart (37 passed)
  • Focused canonical interaction, lifecycle, navigation, and release-compatibility tests.
  • git diff --check

The persistent JPEG encoder adds an asynchronous stage after endOfFrame. Some existing broad widget tests can inspect results before the isolate reply. The same timing failures reproduce on origin/main. The new regression tests use deterministic capture seams and pass.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Slims lifecycle payloads while expanding the 0.8.0 wire-format and replay capture behavior.

Changes:

  • Sends static context only at session start and delta identity data afterward.
  • Removes serialized state identity and adds fresh interaction-owned frames.
  • Updates tests, documentation, and package versions.

Reviewed changes

Copilot reviewed 37 out of 38 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
.gitignore Ignores VS Code settings.
docs/README.md Updates current version.
docs/design/capture-and-fingerprint.md Documents new event and capture semantics.
docs/integration/collector.md Updates collector and suppression behavior.
docs/integration/production-replay-acceptance.md Revises 0.8.0 acceptance criteria.
packages/tugboat/CHANGELOG.md Adds 0.8.0 release notes.
packages/tugboat/README.md Documents compatibility and capture changes.
packages/tugboat/pubspec.yaml Bumps Tugboat to 0.8.0.
packages/tugboat/example/pubspec.yaml Updates example dependency.
packages/tugboat/lib/src/anchor_models.dart Omits state signatures from semantic JSON.
packages/tugboat/lib/src/capture_boundary.dart Reformats paint hashing.
packages/tugboat/lib/src/collector_http_sink.dart Limits lifecycle traits transmission.
packages/tugboat/lib/src/collector_mapper.dart Produces slim lifecycle payloads.
packages/tugboat/lib/src/controller.dart Reworks interaction, route, and scroll captures.
packages/tugboat/lib/src/debug_logging.dart Removes state signatures from logs.
packages/tugboat/lib/src/interaction_transaction.dart Tracks route epochs and capture outcomes.
packages/tugboat/lib/src/models.dart Adds interaction frame trigger and omits state anchors.
packages/tugboat/lib/src/screenshot_capturer.dart Exposes encoder injection and reformats capture code.
packages/tugboat/lib/src/screenshot_encode_isolate.dart Reformats isolate replies.
packages/tugboat/lib/src/sdk_version.dart Updates SDK version constant.
packages/tugboat/lib/src/viewport_semantic_session.dart Revises semantic deduplication and scroll accumulation.
packages/tugboat/test/collector_http_sink_test.dart Updates lifecycle payload expectations.
packages/tugboat/test/collector_mapper_test.dart Tests slim lifecycle mapping.
packages/tugboat/test/replay/interaction_transaction_test.dart Tests route-state cleanup.
packages/tugboat/test/replay/modal_capture_visual_test.dart Stabilizes modal capture timing.
packages/tugboat/test/replay/replay_navigation_interaction_matrix_test.dart Updates state-anchor expectations.
packages/tugboat/test/replay/replay_navigation_race_matrix_test.dart Tests automatic-route attribution.
packages/tugboat/test/replay/replay_overlay_nested_navigation_matrix_test.dart Awaits settled navigation evidence.
packages/tugboat/test/replay/screenshot_encode_isolate_test.dart Reformats isolate test.
packages/tugboat/test/replay/screenshot_fresh_paint_test.dart Reformats fresh-paint test.
packages/tugboat/test/replay_coherence_characterization_test.dart Expands interaction capture coverage.
packages/tugboat/test/scene_inventory_test.dart Updates state-signature assertions.
packages/tugboat/test/scroll_attribution_test.dart Tests scroll callback ordering and programmatic scrolling.
packages/tugboat/test/tugboat_replay_test.dart Tests fresh interaction frames and state omission.
packages/tugboat/test/viewport_semantic_map_test.dart Tests semantic rebuilding and gesture resets.
packages/tugboat_dio/CHANGELOG.md Adds compatibility release notes.
packages/tugboat_dio/README.md Updates dependency documentation.
packages/tugboat_dio/pubspec.yaml Bumps adapter and Tugboat dependency.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

};

if (carriesUserId) {
body['userId'] = userId ?? config.userId;
Comment on lines +3519 to +3520
final resolution = await capture.resolution;
if (!_isCaptureLifecycleCurrent(session, lifecycleEpoch)) return;
Comment on lines +151 to +153
if (scrollContext?.trigger == 'scroll_start') {
_beginScrollSemanticGesture(map);
}
@@ -242,7 +252,6 @@ class TugboatEvent {
if (sessionId != null) 'sessionId': sessionId,
if (captureSessionId != null) 'captureSessionId': captureSessionId,
if (activationRequestId != null) 'activationRequestId': activationRequestId,
static String _routeCaptureKey(String? navigatorId) => navigatorId ?? '';

final Map<Element, _ScrollTracker> _scrollTrackers = {};
final Map<String, InteractionTransaction> _scrollInteractions = {};
route,
lifecycle,
manual,
interaction,
@Chinmay-KB Chinmay-KB changed the title Slim session lifecycle payloads Remove state identity and harden interaction capture Aug 10, 2026
@Chinmay-KB
Chinmay-KB requested a balanced review from Copilot August 10, 2026 18:32

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 39 out of 40 changed files in this pull request and generated no new comments.

Suppressed comments (3)

packages/tugboat/lib/src/controller.dart:3488

  • Completed pointer-linked scrolls are held only in _scrollInteractions, not _activeCompletedGestureCaptures. Clearing this map on session replacement/end or lifecycle deactivation therefore drops the canonical scroll interaction entirely while its capture is pending, contrary to the completed-gesture preservation requirement. Finalize these transactions with the appropriate cancellation reason before clearing the scroll state.
  void _clearScrollCompletionState() {
    _scrollTrackers.clear();
    _scrollInteractions.clear();
    _pendingScrollCompletions.clear();

packages/tugboat/lib/src/viewport_semantic_session.dart:366

  • If the current inventory cannot build a semantic map (for example, semantics are temporarily unavailable or the payload exceeds the limit), maybeEmit leaves the previous _latestMap intact and the tap is resolved against stale controls. Clear the cached map before attempting this current-inventory rebuild so failure resolves as unavailable instead of matching the prior screen.
    if (inventory != null) {
      maybeEmit(inventory, resolver: resolver);
    }

docs/README.md:32

  • The release metadata now declares session wire schema 10, but this compatibility summary still advertises schema 9. Update it so integrators do not configure readers for the obsolete writer version.
- session JSON schema: `9`;

@Chinmay-KB
Chinmay-KB marked this pull request as ready for review August 10, 2026 21:32

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ff0c387aaf

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +3487 to +3488
_scrollInteractions.clear();
_pendingScrollCompletions.clear();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Finalize queued scroll interactions before clearing them

When pointer-up has classified a gesture as a scroll but its scroll-end capture has not resolved, the completed transaction exists only in _scrollInteractions. Ending or replacing the session, or entering an inactive lifecycle state, calls this method after cancelling capture work, so clearing these maps silently drops the transaction without publishing the terminal canonical interaction; unlike completed swipes, these scrolls are not included in _activeCompletedGestureCaptures. Finalize the queued scroll transactions with a cancelled capture outcome before discarding this state.

Useful? React with 👍 / 👎.

Chinmay Kabi and others added 4 commits August 11, 2026 05:02
Remove state-signature plumbing and reshape interaction, route_change, and
scroll collector events as flat facts-only records so downstream consumers
derive outcomes from evidence instead of inferred SDK fields.

Co-authored-by: Cursor <cursoragent@cursor.com>
Fold tap, swipe, scroll, and cancel facts into interaction schema v2 with
a nested gesture payload, and stop emitting scroll_start, scroll_end, and
pointer_cancel as separate production events.

Co-authored-by: Cursor <cursoragent@cursor.com>
Update core docs to describe nested interaction payload gestures and the
removal of scroll_start, scroll_end, and pointer_cancel from production wire.

Co-authored-by: Cursor <cursoragent@cursor.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 61 out of 62 changed files in this pull request and generated 3 comments.

Suppressed comments (2)

packages/tugboat/lib/src/viewport_semantic_session.dart:160

  • This event-level dedupe returns before _recordScrollSemanticSlice. Because every scroll_start now replaces the gesture accumulator, repeating the same semantic slices in a later gesture leaves the new accumulator empty and suppresses its scroll_semantic_snapshot. Record each scroll slice before applying dedupe only to viewport_semantic_map publication.
    final dedupeKey =
        '${map.routeKey}|${map.mapHash}|${map.scrollContext?.dedupeKey ?? ''}';
    if (!_emittedSemanticMaps.add(dedupeKey)) return;

packages/tugboat/test/replay/replay_overlay_nested_navigation_matrix_test.dart:123

  • This helper now searches the entire session and returns the first matching route. The test closes /root twice (dialog, then sheet), so the second lookup can return the earlier dialog-pop event and pass even if the sheet pop emitted nothing. Restore an event-index baseline/cursor and search only events produced after the current action.

@@ -3,7 +3,6 @@ library;
export 'src/anchors.dart'
show
TugboatNormalizedBounds,
static TugboatSession fromJson(Map<String, dynamic> json) {
final version = json['schemaVersion'] as int?;
if (version != 6 && version != 7 && version != 8 && version != 9) {
if (version != 10) {
Comment on lines +59 to +63
'routeChangeSchema': tugboatRouteChangeSchemaVersion,
if (data['fromRoute'] != null) 'fromRoute': data['fromRoute'],
if (data['route'] != null) 'route': data['route'],
if (data['navigation'] != null) 'navigation': data['navigation'],
},
@Chinmay-KB Chinmay-KB changed the title Remove state identity and harden interaction capture Remove legacy interaction formats and state identity Aug 11, 2026
@Chinmay-KB
Chinmay-KB requested a balanced review from Copilot August 11, 2026 08:42

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 61 out of 62 changed files in this pull request and generated no new comments.

Suppressed comments (5)

packages/tugboat/lib/src/viewport_semantic_session.dart:126

  • The reset is reached only after the controller has successfully built a non-null scene inventory. _maybeEmitSceneInventory returns before calling this method when buildSceneInventory returns null (controller.dart:3911-3916), so an unavailable inventory at scroll_start leaves the previous gesture's accumulator alive and a later update can stitch slices across two gestures. Reset from recordScrollStart before inventory construction (for example through a dedicated session API); the new unavailable-path test currently passes a stale non-null inventory and does not cover this case.
    if (scrollContext?.trigger == 'scroll_start') {
      _beginScrollSemanticGesture(
        routeKey: inventory.routeKey,
        scroll: scrollContext!,
      );

packages/tugboat/test/viewport_semantic_map_test.dart:210

  • This assertion only proves that a tap interaction was emitted; it still passes if tap resolution continues using the stale top-button semantic map and never rebuilds after the same-route widget change. Assert that a second semantic-map event is emitted and that its nodes link the interaction's new target fingerprint so this test actually guards the advertised rebuild behavior.
    packages/tugboat/test/replay/replay_navigation_interaction_matrix_test.dart:10
  • The replacement leaves only a direct-controller harness case and removes the real Navigator coverage for named push, replacement, pop, and pushNamedAndRemoveUntil. The overlay suite still exercises widget-backed push/pop, but no widget-backed test now covers replacement or stack cleanup, so regressions in observer callback ordering for those operations would pass despite this PR changing route ownership and causal interaction publication. Retain focused real-widget tests for at least replacement and remove-until.
    packages/tugboat/CHANGELOG.md:11
  • These breaking removals are placed under Unreleased, while the package is being bumped to 0.8.0 and a separate 0.8.0 section follows. Published 0.8.0 release notes would therefore imply that the removed publication modes, aliases, and constructors are not part of 0.8.0. Merge this breaking-changes subsection into the 0.8.0 entry.
## Unreleased

### Breaking changes

- The SDK now publishes only schema-v2 canonical `interaction` gesture events.

docs/design/capture-and-fingerprint.md:205

  • This says state anchors and signatures remain as internal legacy model data, but this PR deletes TugboatStateAnchor and its resolver/model implementation entirely. Update the design document to describe full removal rather than internal retention.
Version 0.8.0 does not write state identity. State anchors and signatures remain
internal legacy model data only. New event, inventory, semantic-map, diagnostic,
debug, and provenance JSON omit them. Use route evidence, target anchors, and
frame hashes for raw replay facts.

@Chinmay-KB
Chinmay-KB merged commit 19d9262 into main Aug 11, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants